/*
Copyright (c) 2020 dxb22@163.com. All rights reserved.
*/

#include <stdbool.h>
#include "cmsis_os2.h"

#include "hi_wifi_api.h"
#include "lwip/lwipopts.h"
#include "lwip/ip_addr.h"
#include "lwip/netifapi.h"
#include "lwip/sockets.h"

#include "http_server.h"

struct RequestMapper *g_mapper = NULL;
uint8_t g_mapperSize = 0;

const char *rspHeader = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\nConnection: close\r\n";

enum HttpServerStatus
{
    HTTP_SERVER_STOPPED = 0,
    HTTP_SERVER_RUNNING,
    HTTP_SERVER_STOPPING
};

typedef struct Connection
{
    int fd;
    struct HttpRequest req;
} Connection;

#define MAX_CONNECTIONS 5
static Connection *g_connections[MAX_CONNECTIONS];

uint8_t g_ServerStatus = HTTP_SERVER_STOPPED;

char *GetAction(struct HttpRequest *req)
{
    uint8_t count = 0;
    bool continuesFlag = false;
    uint32_t actionSize = 0;
    for (; actionSize < req->requestSize && count < 2; actionSize++)
    {
        if (req->buffer[actionSize] == ' ')
        {
            if (!continuesFlag)
            {
                count++;
            }
            continuesFlag = true;
        }
        else
        {
            continuesFlag = false;
        }
    }

    if (count == 2)
    {
        char *result = (char *)malloc(actionSize);
        if (result == NULL)
        {
            printf("Memory Leak!\r\n");
            return NULL;
        }
        memcpy(result, req->buffer, actionSize);
        result[actionSize - 1] = '\0';
        return result;
    }
    else
    {
        return NULL;
    }
}
char *GetHeaderValue(struct HttpRequest *req, const char *name)
{
    char headerName[64] = "\r\n";
    errno_t ret = strcat_s(headerName, 64, name);
    if (ret != ERR_OK)
    {
        return NULL;
    }
    char *header = strstr(req->buffer, headerName);
    if (header == NULL)
    {
        return NULL;
    }
    uint32_t pos = header - req->buffer + strlen(headerName);
    while (req->buffer[pos] == ' ' || req->buffer[pos] == ':')
    {
        pos++;
    }
    char *end = strstr(req->buffer + pos, "\r\n");
    if (end == NULL)
    {
        return NULL;
    }
    uint32_t length = end - req->buffer - pos;
    char *result = (char *)malloc(length + 1);
    if (result == NULL)
    {
        printf("Memory Leak!\r\n");
        return NULL;
    }
    memcpy(result, req->buffer + pos, length);
    result[length] = '\0';
    return result;
}

static char ConvertToValue(char input)
{
    if (input >= '0' && input <= '9')
    {
        return input - '0';
    }
    if (input >= 'a' && input <= 'z')
    {
        return 10 + (input - 'a');
    }
    if (input >= 'A' && input <= 'Z')
    {
        return 10 + (input - 'A');
    }
    printf("%s:Bad code!", __func__);
    return 0;
}

static bool DecodeHTMLFormData(const char *start, const char *end, char *result, uint16_t resultSize)
{
    if (start == NULL || end == NULL || start > end)
    {
        return false;
    }
    if (end - start > MAX_FORMDATA_LENGTH)
    {
        printf("%s:exceed max form data length.", __func__);
        return false;
    }
    if (resultSize < end - start + 1)
    {
        return false;
    }
    int32_t resultIndex = 0;
    for (int32_t i = 0; i < end - start && resultIndex <= resultSize; i++)
    {
        if ((start[i] >= '0' && start[i] <= '9') || (start[i] >= 'a' && start[i] <= 'z') || (start[i] >= 'A' && start[i] <= 'Z') || start[i] == '.' || start[i] == '-' || start[i] == '*' || start[i] == '_')
        {
            result[resultIndex++] = start[i];
            continue;
        }
        switch (start[i])
        {
        case '+':
            result[resultIndex++] = ' ';
            break;
        case '%':
            if (i + 2 >= end - start)
            {
                printf("%s:Bad format found!at %d", __func__, i);
                free(result);
                return false;
            }
            result[resultIndex++] = (ConvertToValue(start[i + 1])) << 4 | (ConvertToValue(start[i + 2]));
            i += 2;
            break;
        default:
            result[resultIndex++] = start[i];
        }
    }
    result[resultIndex] = '\0';
    return true;
}

bool GetFormPara(struct HttpRequest *req, const char *name, char *buff, uint16_t buffSize)
{
    if (req == NULL || name == NULL)
    {
        return false;
    }
    char paraName[32 + 3];
    if (-1 == sprintf_s(paraName, sizeof(paraName), "%s=", name))
    {
        return false;
    }
    char *findStart = req->buffer + req->bodyStartPos;
    char *segmentStart = NULL;
    do
    {
        segmentStart = strstr(findStart, paraName);
        // 找不到直接失败
        if (segmentStart == NULL)
        {
            return false;
        }
        // Body的首个元素，不需要前一个字母是&
        if (segmentStart == req->buffer + req->bodyStartPos)
        {
            break;
        }
        else if (*(segmentStart - 1) == '&')
        {
            break;
        }
        else
        {
            findStart = segmentStart + strlen(paraName);
        }
    } while (true);

    //开始查找结尾
    char *segmentEnd = strstr(segmentStart, "&");
    if (segmentEnd == NULL)
    {
        segmentEnd = req->buffer + req->requestSize;
    }

    return DecodeHTMLFormData(segmentStart + strlen(paraName), segmentEnd, buff, buffSize);
}

/*
ret: when error false; otherwise return true
*/
bool GetHttpPackage(int fd, struct HttpRequest *req)
{
    if (req == NULL)
    {
        return false;
    }
    int contentLength = -1;

    int recvCount = 0;
    if ((recvCount = recv(fd, req->buffer + req->bufferPos, RECV_BUFFER_SIZE - req->bufferPos, 0)) < 0)
    {
        printf("recv error \n");
        return false;
    }
    if (recvCount == 0)
    {
        printf("Connection closed by remote peer!\n");
        return false;
    }
    printf("Recv:%d\r\n", recvCount);
    req->bufferPos += recvCount;
    if (req->bufferPos >= RECV_BUFFER_SIZE - 2)
    {
        printf("Request too long\n");
        return false;
    }

    if (req->buffer[req->bufferPos] != '\0')
    {
        req->buffer[req->bufferPos + 1] = '\0';
    }
    char *headerEndPos = NULL;
    if ((headerEndPos = strstr(req->buffer, "\r\n\r\n")) == NULL)
    {
        return true;
    }

    req->bodyStartPos = headerEndPos - req->buffer + strlen("\r\n\r\n");

    if (contentLength == -1)
    {
        char *strValue = GetHeaderValue(req, "Content-Length");
        if (strValue != NULL)
        {
            sscanf(strValue, "%d", &contentLength);
            free(strValue);
            strValue = NULL;
        }
    }

    if (req->bufferPos < headerEndPos - req->buffer + strlen("\r\n\r\n") + contentLength)
    {
        if (req->bufferPos >= RECV_BUFFER_SIZE - 1)
        {
            printf("Http request too big!");
            return false;
        }
        return true;
    }
    req->requestSize = headerEndPos - req->buffer + strlen("\r\n\r\n") + contentLength;
    return true;
}

void ResponseError(int fd, uint16_t errCode)
{
    const char *codeName = NULL;
    const char *errHeader = "HTTP/1.1 %d %s\r\nContent-Length: 0\r\nConnection: close\r\n";
    switch(errCode) {
        case 404:
        codeName = "Not Found";
        break;
        default:
        codeName = "Unknown";
    }

    char responseBuffer[SEND_BUFFER_SIZE] = "";
    int ret = snprintf(responseBuffer, sizeof(responseBuffer), errHeader, 404, codeName);
    if (ret < 0)
    {
        printf("%s: snprintf failed!\n", __func__);
        return;
    }
    size_t messageSize = ret;
    strcat_s(responseBuffer, sizeof(responseBuffer), "\r\n");
    messageSize += strlen("\r\n");

    printf("Response->%d:%s\n", fd, responseBuffer);
    ret = send(fd, responseBuffer, messageSize, MSG_DONTWAIT);
    if (ret < 0)
    {
        printf("Send response failed!\r\n");
    }
    return;
}

void HttpResponse(int fd, const char *format, ...)
{
    va_list valist;
    va_start(valist, format);

    char responseBuffer[SEND_BUFFER_SIZE] = "";
    char rspData[SEND_BUFFER_SIZE] = "";
    int ret = vsprintf_s(rspData, sizeof(rspData), format, valist);
    if (ret < 0)
    {
        printf("%s: data too big!\n", __func__);
        va_end(valist);
        return;
    }
    va_end(valist);
    printf("Response Data:%s\n", rspData);

    ret = snprintf(responseBuffer, sizeof(responseBuffer), rspHeader, strlen(rspData));
    if (ret < 0)
    {
        printf("%s: snprintf failed!\n", __func__);
        return;
    }
    size_t messageSize = ret;
    strcat_s(responseBuffer, sizeof(responseBuffer), "\r\n");
    messageSize += strlen("\r\n");

    strcat_s(responseBuffer, sizeof(responseBuffer), rspData);
    messageSize += strlen(rspData);

    printf("Response->%d:%s\n", fd, responseBuffer);
    ret = send(fd, responseBuffer, messageSize, MSG_DONTWAIT);
    if (ret < 0)
    {
        printf("Send response failed!\r\n");
    }
    return;
}

static Connection *findConnection(fd_set *readSet)
{
    for (uint32_t i = 0; i < MAX_CONNECTIONS; i++)
    {
        if (g_connections[i] != NULL)
        {
            if (FD_ISSET(g_connections[i]->fd, readSet))
            {
                return g_connections[i];
            }
        }
    }

    return NULL;
}

static void ReleaseConnection(Connection *conn)
{
    if (conn == NULL)
    {
        return;
    }
    for (uint32_t i = 0; i < MAX_CONNECTIONS; i++)
    {
        if (g_connections[i] == conn)
        {
            g_connections[i] = NULL;
        }
    }

    if (conn->fd != -1)
    {
        printf("%s:Release fd %d\n", __func__, conn->fd);
        closesocket(conn->fd);
    }
    free(conn);
}

static void HttpWorkerTask(void *arg)
{
    (void)arg;
    static struct timeval tv = {.tv_sec = 3, .tv_usec = 0};
    while (g_ServerStatus == HTTP_SERVER_RUNNING)
    {
        fd_set fileSet = {0};
        FD_ZERO(&fileSet);
        int maxFD = 0;
        for (uint32_t i = 0; i < MAX_CONNECTIONS; i++)
        {
            if (g_connections[i] != NULL)
            {
                FD_SET(g_connections[i]->fd, &fileSet);
                if (g_connections[i]->fd > maxFD)
                {
                    maxFD = g_connections[i]->fd;
                }
            }
        }
        int ret = select(maxFD + 1, &fileSet, NULL, NULL, &tv);
        printf("%s:select ret %d\n", __func__, ret);
        if (ret == -1)
        {
            osDelay(200);
            continue;
        }
        else if (ret == 0)
        {
            continue;
        }
        Connection *conn = findConnection(&fileSet);
        if (conn == NULL)
        {
            continue;
        }

        if (conn->req.bufferPos < conn->req.requestSize || conn->req.bufferPos >= RECV_BUFFER_SIZE || conn->req.requestSize >= RECV_BUFFER_SIZE)
        {
            printf("%s:wrong status.%d\n", __func__, __LINE__);
            break;
        }
        if (conn->req.bufferPos - conn->req.requestSize == 0)
        {
            conn->req.buffer[0] = '\0';
            conn->req.bufferPos = 0;
            conn->req.requestSize = 0;
        }
        else
        {
            memmove(conn->req.buffer, conn->req.buffer + conn->req.requestSize, conn->req.bufferPos - conn->req.requestSize);
            conn->req.requestSize = 0;
            conn->req.bufferPos = conn->req.bufferPos - conn->req.requestSize;
        }

        if (!GetHttpPackage(conn->fd, &conn->req))
        {
            printf("Read http package failed!\n");
            ReleaseConnection(conn);
            continue;
        }
        /*
        if(conn->req.requestSize >= RECV_BUFFER_SIZE - 2) {
            printf("Package too long!\r\n");
            break;
        }
        conn->req.buffer[conn->req.requestSize+1] = '\0';
        printf("PackageLen=%d\r\n",conn->req.requestSize);

        //printf("Request:%s\r\n", buffer);
        */
        char *action = GetAction(&conn->req);
        if (action == NULL)
        {
            printf("No action found!\n");
            ReleaseConnection(conn);
            continue;
        }
        uint8_t i = 0;
        for (; i < g_mapperSize; i++)
        {
            if (strcmp(action, g_mapper[i].action) == 0)
            {
                g_mapper[i].handler(conn->fd, &conn->req);
            }
        }
        free(action);
        action = NULL;

        // 如果没找到
        if (i >= g_mapperSize)
        {
            ResponseError(conn->fd, 404);
        }
        ReleaseConnection(conn);
    }

    printf("Worker exit!\n");
}

void ShutdownHttpServer(void)
{
    g_ServerStatus = HTTP_SERVER_STOPPING;
}

int RunHTTPServer(struct RequestMapper *mapper, uint8_t mapperSize)
{
    if (mapper == NULL)
    {
        printf("%s[%d]:Bad input para!\n", __func__, __LINE__);
        return -1;
    }
    if (g_ServerStatus != HTTP_SERVER_STOPPED)
    {
        printf("%s:HTTP server not stopped!\n", __func__);
        return -1;
    }
    g_mapper = mapper;
    g_mapperSize = mapperSize;
    memset(g_connections, 0, sizeof(g_connections));
    g_ServerStatus = HTTP_SERVER_RUNNING;

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1)
    {
        printf("%s[%d]listen_fd error\n", __func__, __LINE__);
        g_ServerStatus = HTTP_SERVER_STOPPED;
        return -1;
    }

    struct sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(struct sockaddr_in));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(80);
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    int ret = bind(fd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
    if (ret == -1)
    {
        g_ServerStatus = HTTP_SERVER_STOPPED;
        printf("connot bind server socket\n");
        lwip_close(fd);
        return 1;
    }
    printf("%s:listening!\n", __func__);
    ret = listen(fd, 1);
    if (ret == -1)
    {
        g_ServerStatus = HTTP_SERVER_STOPPED;
        printf("connot listen the client connect request\n");
        lwip_close(fd);
        return 1;
    }

    //启动一个task响应TCP消息
    osThreadAttr_t attr;
    attr.name = "HttpWorkerTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = SEND_BUFFER_SIZE * 2 + RECV_BUFFER_SIZE + 2048;
    attr.priority = osPriorityNormal;

    if (osThreadNew(HttpWorkerTask, NULL, &attr) == NULL)
    {
        printf("[%s]Falied to create HttpWorkerTask!\n", __func__);
    }

    printf("%s:Ready from client!\n", __func__);
    while (g_ServerStatus == HTTP_SERVER_RUNNING)
    {

        struct sockaddr_in clientAddr;
        socklen_t sin_size = sizeof(struct sockaddr_in);
        int clientFd = accept(fd, (struct sockaddr *)&clientAddr, &sin_size);
        if (clientFd == -1)
        {
            perror("accept failed!\n");
            continue;
        }
        printf("%s:new connection\n", __func__);
        Connection *connection = (Connection *)malloc(sizeof(Connection));
        if (connection == NULL)
        {
            printf("%s:oom\n", __func__);
            continue;
        }
        for (uint32_t i = 0; i < MAX_CONNECTIONS; i++)
        {
            if (g_connections[i] == NULL)
            {
                printf("%s:New fd %d in index %d\n", __func__, clientFd, i);
                connection->fd = clientFd;
                (void)memset(&connection->req, 0, sizeof(connection->req));
                g_connections[i] = connection;
                connection = NULL;
                break;
            }
        }

        if (connection != NULL)
        {
            free(connection);
            printf("%s:too much connections!\n");
        }
    }
    // TODO:release all socket？
    for (uint32_t i = 0; i < MAX_CONNECTIONS; i++) {
        if (g_connections[i] != NULL) {
            ReleaseConnection(g_connections[i]);
        }
    }

    g_mapper = NULL;
    g_mapperSize = 0;
    g_ServerStatus = HTTP_SERVER_STOPPED;

    return 0;
}
